package com.common.zrd.validation.support;


import com.common.zrd.validation.errorcode.ErrorCode;
import com.common.zrd.validation.errorcode.ErrorCodes;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.util.*;

public class ErrorCodeResourceLoader {
    private static final String EXTENSION = ".properties";
    private static final String[] NLSUFFIXES;
    private static final Object ASSIGNED = new Object();
    private static final Object[] EMPTY_ARGS = new Object[0];
    private static final int MOD_EXPECTED = 9;
    private static final int MOD_MASK = 25;

    static {
        String nl = Locale.getDefault().toString();
        List<String> result = new ArrayList(4);
        for (; ; ) {
            result.add('_' + nl + ".properties");
            int lastSeparator = nl.lastIndexOf('_');
            if (lastSeparator == -1) {
                break;
            }
            nl = nl.substring(0, lastSeparator);
        }
        result.add(".properties");
        NLSUFFIXES = result.toArray(new String[result.size()]);
    }

    public static void load(String bundleName, Class<? extends ErrorCodes> clazz, Charset charset) {
        if ((bundleName == null) || (bundleName.isEmpty())) {
            throw new IllegalArgumentException("Bundle name is empty.");
        }
        if ((clazz == null) || (clazz == ErrorCodes.class)) {
            throw new IllegalArgumentException("ErrorCodes clazz is null.");
        }

        long start = System.currentTimeMillis();

        Field[] fieldArray = clazz.getDeclaredFields();

        ClassLoader loader = clazz.getClassLoader();

        boolean isAccessible = (clazz.getModifiers() & 0x1) != 0;

        int len = fieldArray.length;
        Map<String, Object> fields = new LinkedHashMap(len * 2);
        Field[] arrayOfField1 = fieldArray;
        int i = arrayOfField1.length;
        for (Field localField1 : arrayOfField1) {
            fields.put(localField1.getName(), localField1);
        }
        String[] variants = buildVariants(bundleName);
        String[] arrayOfString1 = variants;
        int localField1 = arrayOfString1.length;
        for (String str : arrayOfString1) {
            String variant = str;
            InputStream input = loader.getResourceAsStream(variant);
            if (input != null) {
                try {
                    MessagesProperties properties = new MessagesProperties(fields, bundleName, isAccessible);
                    properties.load(new Native2AsciiConvertInputStream(input, charset));
                    try {
                        input.close();
                    } catch (IOException localIOException1) {
                    }
//                    aFieldArray++;
                } catch (IOException e) {
                } finally {
                    try {
                        input.close();
                    } catch (IOException localIOException3) {
                    }
                }
            }
        }
        computeMissingMessages(bundleName, clazz, fields, fieldArray, isAccessible);
    }

    private static String[] buildVariants(String root) {
        root = root.replace('.', '/');
        String[] variants = new String[NLSUFFIXES.length];
        for (int i = 0; i < variants.length; i++) {
            variants[i] = (root + NLSUFFIXES[i]);
        }
        return variants;
    }

    private static void computeMissingMessages(String bundleName, Class<? extends ErrorCodes> clazz, Map<String, Object> fieldMap, Field[] fieldArray, boolean isAccessible) {
        for (Field field : fieldArray) {
            Class<?> fieldType = field.getType();
            if (fieldType.equals(ErrorCode.class) ?
                    (field.getModifiers() & 0x9) == 9 :

                    (field.getModifiers() & 0x19) == 9) {
                if (fieldMap.get(field.getName()) != ASSIGNED) {
                    try {
                        fillField(field, isAccessible, null, bundleName);
                    } catch (Exception e) {

                    }
                }
            }
        }
    }

    private static void fillField(Field field, boolean isAccessible, String value, String bundleName)
            throws Exception {
        if (!isAccessible) {
            field.setAccessible(true);
        }
        Object existValue = field.get(null);
        if ((existValue instanceof CustomerErrorCodeDefine)) {
            CustomerErrorCodeDefine ced = (CustomerErrorCodeDefine) existValue;
            String errorCodeKey = ced.getErrorCode();
            if ((errorCodeKey == null) || (errorCodeKey.isEmpty())) {
                ced.setErrorCodeKey(field.getName());
            }
        } else if ((existValue instanceof SystemErrorCodeDefine)) {
            SystemErrorCodeDefine sed = (SystemErrorCodeDefine) existValue;
            String errorCodeKey = sed.getErrorCode();
            if ((errorCodeKey == null) || (errorCodeKey.isEmpty())) {
                sed.setErrorCodeKey(field.getName());
            }
            if (value == null) {
                value = "NLS missing message: " + field.getName() + " in: " + bundleName;

            }
            sed.setErrorCodeMessage(value);
        } else if ((field.getModifiers() & 0x19) == 9) {
            Class<?> fieldType = field.getType();
            if (ErrorCode.class.equals(fieldType)) {
                if (value == null) {
                    value = "NLS missing message: " + field.getName() + " in: " + bundleName;

                }
                field.set(null, new SystemErrorCodeDefine(field.getName(), value));
            }
        }
    }

    static String internalBind(String message, Object... bindings) {
        if (message == null) {
            return "No message available.";
        }
        int argsLength = bindings == null ? 0 : bindings.length;
        Object[] args;
        if (argsLength == 0) {
            args = EMPTY_ARGS;
        } else {
            args = Arrays.copyOf(bindings, argsLength);
        }
        int length = message.length();
        StringBuilder buffer = new StringBuilder(length + args.length * 5);
        for (int i = 0; i < length; i++) {
            char c = message.charAt(i);
            switch (c) {
                case '{':
                    int index = message.indexOf('}', i);
                    if (index == -1) {
                        buffer.append(c);
                    } else {
                        i++;
                        if (i >= length) {
                            buffer.append(c);
                        } else {
                            try {
                                int number = Integer.parseInt(message.substring(i, index));
                                if ((number >= args.length) || (number < 0)) {
                                    buffer.append("<missing argument>");
                                } else {
                                    buffer.append(args[number]);
                                }
                            } catch (NumberFormatException localNumberFormatException) {
                                buffer.append('{');
                                buffer.append(message, i, index);
                                buffer.append('}');
                            }
                            i = index;
                        }
                    }
                    break;
                case '\'':
                    int nextIndex = i + 1;
                    if (nextIndex >= length) {
                        buffer.append(c);
                    } else {
                        char next = message.charAt(nextIndex);
                        if (next == '\'') {
                            i++;
                            buffer.append(c);
                        } else {
                            index = message.indexOf('\'', nextIndex);
                            if (index == -1) {
                                buffer.append(c);
                            } else {
                                buffer.append(message, nextIndex, index);
                                i = index;
                            }
                        }
                    }
                    break;
                default:
                    buffer.append(c);
            }
        }
        return buffer.toString();
    }

    private static class MessagesProperties
            extends Properties {
        private static final long serialVersionUID = 1L;
        private final String bundleName;
        private final Map<String, Object> fields;
        private final boolean isAccessible;

        MessagesProperties(Map<String, Object> fieldMap, String bundleName, boolean isAccessible) {
            this.fields = fieldMap;
            this.bundleName = bundleName;
            this.isAccessible = isAccessible;
        }

        @Override
        public synchronized Object put(Object key, Object value) {
            Object fieldObject = this.fields.put((String) key, ErrorCodeResourceLoader.ASSIGNED);
            if (fieldObject == ErrorCodeResourceLoader.ASSIGNED) {
                return null;
            }
            if (fieldObject == null) {

                return null;
            }
            Field field = (Field) fieldObject;
            if ((field.getModifiers() & 0x9) != 9) {
                return null;
            }
            try {
                ErrorCodeResourceLoader.fillField(field, this.isAccessible, (String) value, this.bundleName);
            } catch (Exception e) {

            }
            return null;
        }
    }
}
